<template>
  <el-form label-width="auto" style="max-width: 600px">
    <el-form-item label="请输入消息">
      <el-input v-model="messageText.content" type="textarea"/>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="viewSendMsg()">发送</el-button>
      <el-button type="danger" @click="disableWebSocket()">断开连接</el-button>
      <el-button type="info" @click="initWebSocket()">重新连接</el-button>
      <el-button type="success" @click="invite()">发起通话</el-button>
    </el-form-item>
  </el-form>

  <div class="flexChild" id="camera-container" align="center">
    <div class="camera-box">
      <video id="received_video" autoplay></video>
      <video id="local_video" autoplay muted></video>
      <el-button @click="hangUpCall()" :disabled="hangUpCallBtn">Hang Up</el-button>
    </div>
  </div>
</template>

<script setup>
import {onBeforeUnmount, onMounted, reactive, ref} from "vue";

let messageText = reactive({
  from: 1,
  to: 2,
  type: 0,
  groupId: null,
  content: ''
})

let receiveMsg = reactive({
  from: null,
  to: null,
  type: null,
  groupId: null,
  content: null
})

let websocket = null
let myPeerConnection = null
let hangUpCallBtn = ref(true)

let iceCandidate = reactive({
  sdpMLineIndex: null,
  candidate: '',
  usernameFragment: '',
  sdpMid: ''
})

let sdpObj = reactive({
  type: '',
  sdp: ''
})


const initWebSocket = () => {
  websocket = new WebSocket('ws://192.168.1.99:8004/webrtc');
  websocket.onopen = webSocketOnOpen
  websocket.onmessage = webSocketOnMessage
  websocket.onclose = webSocketOnClose
  websocket.onerror = webSocketOnError
}

const webSocketOnOpen = (event) => {
  console.log('建立连接')
  let messageText = {
    from: 1,
    type: 0
  }
  sendMsg(messageText);
}

const webSocketOnMessage = (event) => {
  Object.assign(receiveMsg, JSON.parse(event.data))

  //聊天消息
  if (receiveMsg.type === 1) {
    console.info("收到消息：" + receiveMsg.content);
    //收到提议
  } else if (receiveMsg.type === 2) {
    console.info("收到提议：" + receiveMsg.content);
    handleVideoOfferMsg(receiveMsg)
    //收到应答
  } else if (receiveMsg.type === 3) {
    console.info("收到应答：" + receiveMsg.content);
    handleRemoteDescription(receiveMsg)
    //收到候选
  } else if (receiveMsg.type === 4) {
    console.info("收到候选：" + receiveMsg.content);
    handleAddICECandidateMsg(receiveMsg)
    //收到挂断
  } else if (receiveMsg.type === 5) {
    console.info("收到挂断：" + receiveMsg.content);
    handleRemoveTrackEvent()
  }
}

const webSocketOnClose = (event) => {
  console.error('连接关闭');
}

const webSocketOnError = (event) => {
  console.error('连接错误');
}

const viewSendMsg = () => {
  let message = {
    from: 1,
    to: 2,
    type: 1,
    content: messageText.content
  }
  sendMsg(message)
}

const sendMsg = (messageText) => {
  websocket.send(JSON.stringify(messageText))
  if (messageText.content) {
    console.info('发送消息：' + messageText.content)
  }
}

const disableWebSocket = () => {
  let messageText = {
    from: 1,
    type: -1
  }
  sendMsg(messageText)
  handleRemoveTrackEvent()
  websocket.close()
}

let hasAddTrack = false;
let mediaConstraints = {
  audio: true, // We want an audio track
  video: false, // ...and we want a video track
};

//开始一个通话
const invite = (evt) => {
  if (myPeerConnection) {
    alert("已经在通话中!");
  } else {
    if (messageText.from === messageText.to) {
      alert(
          "不能呼叫自己",
      );
      return;
    }

    createPeerConnection();

    navigator.mediaDevices
        .getUserMedia(mediaConstraints)
        .then(gotStream)
        .catch(handleGetUserMediaError);
  }
}

//收到提议
const handleVideoOfferMsg = (messageText) => {
  Object.assign(sdpObj, JSON.parse(messageText.content))

  createPeerConnection();

  let desc = new RTCSessionDescription(sdpObj);

  myPeerConnection.setRemoteDescription(desc).then(function () {
    return navigator.mediaDevices.getUserMedia(mediaConstraints);
  })
      .then(gotStream)
      .then(function () {
        return myPeerConnection.createAnswer();
      })
      .then(function (answer) {
        return myPeerConnection.setLocalDescription(answer);
      })
      .then(function () {
        let messageText = {
          from: 1,
          to: 2,
          type: 3,
          content: myPeerConnection.localDescription
        }
        sendMsg(messageText);
      })
      .catch(handleGetUserMediaError);
}

//设置远程描述
const handleRemoteDescription = (messageText) => {
  Object.assign(sdpObj, JSON.parse(messageText.content))

  let desc = new RTCSessionDescription(sdpObj);
  myPeerConnection.setRemoteDescription(desc).catch(handleGetUserMediaError);
}

//创建端到端连接
const createPeerConnection = () => {
  myPeerConnection = new RTCPeerConnection({
    iceServers: [     // Information about ICE servers - Use your own!
      {
        urls: "stun:stun.stunprotocol.org"
      }
    ]
  });

  hasAddTrack = myPeerConnection.addTrack !== undefined;

  myPeerConnection.onicecandidate = handleICECandidateEvent;
  myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
  myPeerConnection.onremovetrack = handleRemoveTrackEvent;
  myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
  myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
  myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;

  if (hasAddTrack) {
    myPeerConnection.ontrack = handleAddTrackEvent;
  } else {
    myPeerConnection.onaddstream = handleAddStreamEvent;
  }

}

//发送 ICE 候选
const handleICECandidateEvent = (event) => {
  if (event.candidate) {
    let messageText = {
      from: 1,
      to: 2,
      type: 4,
      content: event.candidate
    }
    sendMsg(messageText)
  }
}

//接收 ICE 候选
const handleAddICECandidateMsg = (messageText) => {
  Object.assign(iceCandidate, JSON.parse(messageText.content))
  let candidate = new RTCIceCandidate(iceCandidate);
  myPeerConnection.addIceCandidate(candidate).catch(reportError);
}

//开始协商
const handleNegotiationNeededEvent = () => {
  myPeerConnection
      .createOffer()
      .then(function (offer) {
        return myPeerConnection.setLocalDescription(offer);
      })
      .then(function () {
        let messageText = {
          from: 1,
          to: 2,
          type: 2,
          content: myPeerConnection.localDescription
        }
        sendMsg(messageText)
      })
      .catch(reportError);
}

//接收新的流数据
const handleAddStreamEvent = (event) => {
  document.getElementById("received_video").srcObject = event.stream;
  hangUpCallBtn.value = false;
}

//接收新的流数据
const handleAddTrackEvent = (event) => {
  document.getElementById("received_video").srcObject = event.streams[0];
  hangUpCallBtn.value = false;
}

// 处理流的移除
const handleRemoveTrackEvent = (event) => {
  let stream = document.getElementById("received_video").srcObject;
  let trackList = stream.getTracks();

  if (trackList.length == 0) {
    closeVideoCall();
  }
}

//挂机
const hangUpCall = () => {
  closeVideoCall();
  let messageText = {
    from: 1,
    to: 2,
    type: 5
  }
  sendMsg(messageText)
}

//结束通话
const closeVideoCall = () => {
  let remoteVideo = document.getElementById("received_video");
  let localVideo = document.getElementById("local_video");

  if (myPeerConnection) {
    myPeerConnection.ontrack = null;
    myPeerConnection.onremovetrack = null;
    myPeerConnection.onremovestream = null;
    myPeerConnection.onicecandidate = null;
    myPeerConnection.oniceconnectionstatechange = null;
    myPeerConnection.onsignalingstatechange = null;
    myPeerConnection.onicegatheringstatechange = null;
    myPeerConnection.onnegotiationneeded = null;

    if (remoteVideo.srcObject) {
      remoteVideo.srcObject.getTracks().forEach((track) => track.stop());
    }

    if (localVideo.srcObject) {
      localVideo.srcObject.getTracks().forEach((track) => track.stop());
    }

    myPeerConnection.close();
    myPeerConnection = null;
  }

  remoteVideo.removeAttribute("src");
  remoteVideo.removeAttribute("srcObject");
  localVideo.removeAttribute("src");
  remoteVideo.removeAttribute("srcObject");

  hangUpCallBtn.value = true;
}

//ICE 连接状态
const handleICEConnectionStateChangeEvent = (event) => {
  switch (myPeerConnection.iceConnectionState) {
    case "closed":
    case "failed":
    case "disconnected":
      closeVideoCall();
      break;
  }
}

//ICE 信令状态
const handleSignalingStateChangeEvent = (event) => {
  switch (myPeerConnection.signalingState) {
    case "closed":
      closeVideoCall();
      break;
  }
}

//ICE 收集状态
const handleICEGatheringStateChangeEvent = (event) => {
  // Our sample just logs information to console here,
  // but you can do whatever you need.
}

//获取流
const gotStream = (stream) => {
  document.getElementById("local_video").srcObject = stream;

  if (hasAddTrack) {
    let trackList = stream.getTracks();
    if (trackList.length > 0) {
      trackList.forEach(track => myPeerConnection.addTrack(track, stream));
    }
  } else {
    console.log(
        "你的浏览器不支持 RTCPeerConnection.addTrack()。正在退回到<strong>已弃用</strong>的 addStream() 方法…",
    );
    myPeerConnection.addStream(stream);
  }
}

//处理 getUserMedia() 错误
const handleGetUserMediaError = (e) => {
  switch (e.name) {
    case "NotFoundError":
      alert(
          "Unable to open your call because no camera and/or microphone" +
          "were found.",
      );
      break;
    case "SecurityError":
    case "PermissionDeniedError":
      // Do nothing; this is the same as the user canceling the call.
      break;
    default:
      alert("Error opening your camera and/or microphone: " + e.message);
      break;
  }

  closeVideoCall();
}

onMounted(() => {
  initWebSocket()
})

onBeforeUnmount(() => {
  disableWebSocket()
  handleRemoveTrackEvent()
})
</script>

<style scoped>
.homepage-hero-module,
.video-container {
  position: relative;
  height: 100vh;
  overflow: hidden;
}

.video-container .poster img {
  z-index: 0;
  position: absolute;
}

.video-container .filter {
  z-index: 1;
  position: absolute;
  /*background: rgba(0, 0, 0, 0.4);*/
  width: 100%;
}

.fillWidth {
  width: 100%;
}
</style>
